/*
 *  Copyright (C) 2010 Glauco
 * 
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.balisunrise.swing.commons;

import org.balisunrise.annotation.FilterType;
import org.balisunrise.application.Application;
import org.balisunrise.builder.FactoryException;
import org.balisunrise.common.Bindable;
import org.balisunrise.common.Criterion;
import org.balisunrise.common.BindingGroup;
import org.balisunrise.common.FieldComponent;

/**
 * Componente que representa um campo (Atributo) de uma entidade (Classe) que
 * pode ser utilizado como um Criterion de uma pesquisa.
 *
 * @author Glauco
 */
public class CriterionablePanelField extends PanelField
        implements org.balisunrise.common.CriterionablePanelField{

    public static final String PROP_VALUE = "value";
    public static final String PROP_CRITERION_VALUE = "criterionValue";
    public static final String PROP_FIELD_PRE_NAME = "fieldPreName";
    public static final String PROP_FILTER_TYPE = "filterType";

    /**
     * Valor do campo da entidade pesquisada.
     */
    private Object value;

    /**
     * Valor do campo para realizar a pesquisa, o qual vai para tela.
     */
    private Object criterionValue;

    /**
     * Antigo valor do campo para realizar a pesquisa.
     */
    private Object oldCV;

    /**
     * Nome predecessor do nome do campo para filtrar.
     */
    private String fieldPreName;

    /**
     * Tipo do filtro para seleção.
     */
    private FilterType filterType;

    /**
     * Criterion para seleção.
     */
    private Criterion criterion;

    /**
     * Retorna o valor do campo da entidade.
     */
    public Object getValue() {
        return value;
    }

    /**
     * Atribui o valor do campo da entidade.
     * @param value
     */
    public void setValue(Object value) {
        Object oldValue = this.value;
        this.value = value;
        changeSupport.firePropertyChange(PROP_VALUE, oldValue, value);

        setCriterionValue(getValue());
    }

    /**
     * Retorna o valor do campo para pesquesa.
     */
    public Object getCriterionValue() {
        return criterionValue;
    }

    /**
     * Atribui o valor do campo para pesquisa.
     */
    public void setCriterionValue(Object criterionValue) {
        Object old = this.criterionValue;
        this.criterionValue = criterionValue;
        changeSupport.firePropertyChange(
                PROP_CRITERION_VALUE, old, criterionValue);
    }

    /**
     * Get fieldPreName.
     */
    public String getFieldPreName() {
        return fieldPreName;
    }

    /**
     * Set fieldPreName.
     */
    public void setFieldPreName(String fieldPreName) {
        String old = this.fieldPreName;
        this.fieldPreName = fieldPreName;
        changeSupport.firePropertyChange(
                PROP_FIELD_PRE_NAME, old, fieldPreName);
    }

    /**
     * Get filterType.
     */
    public FilterType getFilterType() {
        return filterType;
    }

    /**
     * Set filterType
     */
    @Override
    public void setFilterType(FilterType filterType) {
        FilterType oldFilterType = this.filterType;
        this.filterType = filterType;
        changeSupport.firePropertyChange(
                PROP_FIELD_PRE_NAME, oldFilterType, filterType);
    }

    /**
     * Adiciona um nome predecessor ao nome do field.
     * É utilizado para filtrar atravéz de campos encapsulados por entidades
     * de entidades.
     * Por Exemplo:
     * A Classe Estado tem uma referencia a Pais, então para filtrar os
     * estados pelo nome do pais usariamos "pais.nome" onde "nome" seria o
     * campo e "pais" seria o pré-name.
     *
     * @param prefix
     */
    @Override
    public void addPrefix(String prefix) {
        if (fieldPreName == null || fieldPreName.isEmpty())
            setFieldPreName(prefix);
        else
            setFieldPreName(prefix + "." + fieldPreName);
    }

    /**
     * Vincula o value deste componente ao source.
     */
    @Override
    public void bindComponent(BindingGroup bindingGroup,
            Object source, String property) {

        String sourceProperty = property + '.' + getFieldName();

        // Propriedade do source para bind deste objeto.
        Application.get().getBindingBuilder()
                .createBinding(bindingGroup,
                source, sourceProperty, this, PROP_VALUE);

        // bind do valor criterionavel.
        for (FieldComponent fc : fields) {
            if(fc instanceof Bindable)
                ((Bindable)fc).bindComponent(
                        bindingGroup, this, PROP_CRITERION_VALUE);
        }
    }

    /**
     * Retorna um Criterion.
     * @param prefix Prefixo para ser utilizado como preNames na construção do
     * Criterion.
     * @return Retorna um Criterion.
     */
    @Override
    public Criterion getCriterion() {

        if (criterion == null){
            try{
                criterion = Application.get()
                        .getFactory()
                        .createBaliCriterion(getFieldName());
                if(fieldPreName != null && fieldPreName.length() > 0){
                    if(fieldPreName.contains("."))
                        for (String s : fieldPreName.split(".")) {
                            criterion.addPreName(s);
                        }
                    else
                        criterion.addPreName(fieldPreName);
                }
            }catch (FactoryException fex){
                System.out.println("Falha ao criar BaliCriterion durante" +
                        " SelectionPanelFiels.getCriterion:\n" + fex.getMessage());
            }
        }

        oldCV = getCriterionValue();

        criterion.setFilterType(getFilterType());
        criterion.setEmptyForAll(false);
        criterion.setValue(getCriterionValue());

        return criterion;
    }

    /**
     * Não utilizado por CriterionablePanelField
     * @param emptyForAll
     */
    @Override
    public void setEmptyForAll(boolean emptyForAll) {
        // nop
    }

    /**
     * Verifica se os valores do filtro foram modificados desde a última
     * chamada getCriterion().
     */
    @Override
    public boolean isModified() {
        return (!(criterionValue == null && value == null) &&
                !(criterionValue != null && criterionValue.equals(value)));
    }

    /**
     * Não permite.
     * @return false.
     */
    @Override
    public boolean setEnabled(boolean value) {
        return false;
    }
}
